package parserjc.jaccie;

/**
 * @author Ralf Meyer,
 * "Erstellung von Compiler Compiler Komponenten f�r die Zielsprache Java"
 * UniBwM - ID26/96
 *
 * The following methods are implemented by Volker Seibt:
 * isBlocked
 * setBlocked
 * blockLeftMost
 * unblockRightMost
 * insertLeftMost
 * cutSubtree
 *
 */

import java.util.*;
import java.io.*;

public final class SICTree extends SICData implements Cloneable {
    protected int state = 0;
    protected String value = null;
    protected Hashtable attributes = null;
    protected SICTree father = null;
    protected Vector sons = null;

    /**
     * If the tree is blocked the insertLeftMost-method
     * while not use this tree to insert the given rule.
     */
    protected boolean blocked = false;


    private SICTree() {};

    public SICTree( String aNodeValue ) {
        value = aNodeValue;
        sons = new Vector();
    }

    public static SICTree newNode( String aNodeValue ) throws NullPointerException {
        if (aNodeValue==null) throw new NullPointerException("null NodeValue specified");
          else {
            SICTree tree = new SICTree();
            tree.value = aNodeValue;
            tree.sons = new Vector();
            return tree;
          }
    }

    public static SICTree newLeaf( String aLeafValue,String anAttributeValue ) throws NullPointerException{
        if (aLeafValue==null) throw new NullPointerException("null LeafValue specified");
        else {
            SICTree tree = new SICTree();
            tree.value = aLeafValue;
            if (anAttributeValue != null)
                tree.addAttribute("string","true",anAttributeValue);
            return tree;
        }
    }

    public static SICTree loadTree( String einFileName ) throws IOException {
        return loadTree(new File(einFileName));
    }

    /**
    * angepasst : 06.2003 - used deprecated methods of String
    */
    public static SICTree loadTree( File einFile ) throws IOException {
        char[] fileData;
        FileReader fileReader = new FileReader(einFile);
        int size = (int)einFile.length();
        fileData = new char[size];
	    int count = 0;
	    //Einlesen
        while(count < size)
            count += fileReader.read(fileData,count,size-count);
	    //Schliessen
	    fileReader.close();

        String cr = System.getProperties().getProperty("line.separator");
        ////////////////////////////////////////////////
        String treeString = new String(fileData);
        ////////////////////////////////////////////////
        StringTokenizer strTkzr = new StringTokenizer(treeString," " +cr,false);
        Vector treeVector = new Vector();
        String element;
        while (strTkzr.hasMoreElements()) {
            element = (String)strTkzr.nextElement();
            if (element.equals("ListIndex:")) break;
            if (element.equals("Tree:")==false) treeVector.addElement(element);
        }
        return SICTree.buildTreeFrom(treeVector);
    }

    public static SICTree buildTreeFrom( String treeString ) throws NullPointerException {
        if (treeString==null) throw new NullPointerException("null treeString specified");
          else {
            String cr = System.getProperties().getProperty("line.separator");
            StringTokenizer strTkzr = new StringTokenizer(treeString," " + cr,false);
            Vector treeVector = new Vector();
            while (strTkzr.hasMoreElements())
                treeVector.addElement((String)strTkzr.nextElement());
            return SICTree.buildTreeFrom(treeVector);
        }
    }

    private static SICTree buildTreeFrom( Vector treeVector ) {
        SICTree tree;
        boolean isNode = false;
        String element = (String)treeVector.firstElement();
        treeVector.removeElementAt(0);
        if (treeVector.size()>0) if (((String)treeVector.firstElement()).equals("(")) isNode = true;
        if (isNode) {
            tree = SICTree.newNode(element);
            treeVector.removeElementAt(0);
            while (true)
                if (treeVector.size()==0) break;
                  else {
                    element = (String)treeVector.firstElement();
                    if (element.equals(")")) break;
                    tree.addSonRight(buildTreeFrom(treeVector));
                  }
            treeVector.removeElementAt(0);}
          else {
            int index = element.indexOf(171);
            if (index==-1) tree = SICTree.newLeaf(element,null);
              else tree = SICTree.newLeaf(element.substring(0,index),
                                          element.substring(index+1,element.length()-1));
        }
        return tree;
    }

    /**
     * @return true if the tree is blocked.
     */
    public boolean isBlocked() {
        return blocked;
    }

    /**
     * Blocks or unblocks the tree.
     */
    public void setBlocked(boolean block) {
        this.blocked = block;
    }

    /**
     * Blocks the left-most occurence of the given node-value.
     */
    public boolean blockLeftMost(String nodeName) {
        if (this.isLeaf()) {
          if(this.value.equals(nodeName) && !this.isBlocked()) {
             this.setBlocked(true);
             return true;
          }
        }
        else {
            for (int i=0; i < this.sons.size(); i++)
              if (((SICTree)sons.elementAt(i)).blockLeftMost(nodeName))
                 return true;
        }
        return false;
    }


    /**
     * Unblocks the right-most blocked occurence of the given node-name.
     */
    public boolean unblockRightMost(String nodeName) {
        if (this.isLeaf()) {
           if (this.value.equals(nodeName) && this.isBlocked()) {
                this.setBlocked(false);
                return true;
           }
       }
       else {
          for (int i=this.sons.size()-1; i >= 0; i--)
              if (((SICTree)sons.elementAt(i)).unblockRightMost(nodeName))
                 return true;

       }
       return false;
    }

     /**
      * Inserts the ride side of the given rule at
      * the left-most occurence of the rules left side in this.
      */
    public boolean insertLeftMost(String[] rule) {
       if (this.isLeaf()) {
           if (this.value.equals(rule[0]) && !this.isBlocked()) {
               sons = new Vector(); // may be epsilon
               for (int i=1; i<rule.length ; i++) {
                   this.addSonRight(newLeaf(rule[i],null));
               }
               return true;
           }
       }
       else {
          for (int i=0; i < this.sons.size(); i++) {
              if (((SICTree)sons.elementAt(i)).insertLeftMost(rule))
                 return true;
          }
       }
       return false;
    }

     /**
      * Inserts the ride side of the given rule at
      * the left-most occurence of the rules left side in this.
      */
    public boolean insertLeftMost(Vector rule) {
       if (this.isLeaf()) {
           if (this.value.equals(((SICTree)rule.elementAt(0)).getValue()) &&
               !this.isBlocked()) {
               sons = new Vector(); // may be epsilon
               for (int i=1; i<rule.size(); i++) {
                   this.addSonRight((SICTree)rule.elementAt(i));
               }
               return true;
           }
       }
       else {
          for (int i=0; i < this.sons.size(); i++) {
              if (((SICTree)sons.elementAt(i)).insertLeftMost(rule))
                 return true;
          }
       }
       return false;
    }

    /**
     * Searches for the first occurence of a subtree with
     * rule[0] as root and rule[1], rule[2],... as leafs and
     * cuts the leafs.
     * @returns The (cutted) subtree or null if such tree was not found.
     */
    public SICTree cutSubtree(String[] rule) {
        if (this.isNode()) {
            if (this.value.equals(rule[0]) ) {
                if (this.sons.size() == rule.length-1) {
                    boolean success = true;
                    SICTree son;
                    for (int i=0; i < sons.size(); i++) {
                        son = (SICTree)sons.elementAt(i);
                        success = (son.getValue().equals(rule[i+1]) && son.isLeaf());
                        if (!success) break;
                    }
                    if (success) {
                        this.sons = null;
                        return this;
                    }
                }
            }
            // no success yet
            SICTree result;
            for (int i=sons.size()-1; i >= 0; i--) {
                   result = ((SICTree)sons.elementAt(i)).cutSubtree(rule);
                   if (result != null) return result;
            }
        }
        return null;
    }


    public void cutFather() {
        father = null;
    }

     public SICTree getFather() {
        return father;
    }

    public void addSonRight( SICTree aTree ) {
        aTree.father = this;
        if (sons==null) sons = new Vector(1);
        sons.addElement(aTree);
    }

    public void addSonLeft( SICTree aTree ) {
        aTree.father = this;
        if (sons==null) addSonRight(aTree);
          else sons.insertElementAt(aTree,0);
    }

    public void setState( int aStateNumber ) {
        state = aStateNumber;
    }

    public String getValue() {
        return value;
    }

    public int getState() {
        return state;
    }

    public Vector getSons() {
        return sons;
    }

    public boolean isNode() {
        return sons!=null;
    }

    public boolean isLeaf() {
        return sons==null;
    }


    public String toString() {
        return toString("",false,false);
    }

    public String toString( boolean withState,boolean withAttributeState ) {
        return toString("",withState,withAttributeState);
    }

    private String toString( String space,boolean withState,boolean withAttributeState ) {
        String cr = "\n";//System.getProperties().getProperty("line.separator");
        StringBuffer help = new StringBuffer(space);
        if (withState) {
            help.append("(");
            help.append(state);
            help.append(") ");
        }
        help.append(value);
        if (attributes==null) help.append(cr);
          else {
            Enumeration enu = attributes.keys();
            if (enu.hasMoreElements()) {
                String key = (String)enu.nextElement();
                help.append(" - " + key + ": " + getValueOfAttribute(key));
                if (withAttributeState) help.append(" (" + getStateOfAttribute(key) + ")");
                help.append(cr);
                while (enu.hasMoreElements()) {
                    key = (String)enu.nextElement();
                    help.append(space);
                    if (withState)
                        for (int i=0; i<Integer.toString(state).length()+3; i++) help.append(" ");
                    for (int i=0; i<value.length()+3; i++) help.append(" ");
                    help.append(key + ": " + getValueOfAttribute(key));
                    if (withAttributeState) help.append(" (" +getStateOfAttribute(key) + ")");
                    help.append(cr);
                }
            }
          }

        if (sons!=null)
            for (int i=0; i<sons.size(); i++)
                help.append(((SICTree)sons.elementAt(i)).toString(space + "  ",
                                                                  withState,withAttributeState));
        return help.toString();
    }


    private String treeString() {
        StringBuffer strBuffer = new StringBuffer(value);
        if (sons==null) {
            Object value = getValueOfAttribute("string");
	    if (value!=null) strBuffer.append((char)171 + value.toString() + (char)187);
            strBuffer.append(" "); }
          else {
            strBuffer.append(" ( ");
            for (int i=0; i<sons.size(); i++)
                strBuffer.append(((SICTree)sons.elementAt(i)).treeString());
            strBuffer.append(") ");
        }
        return strBuffer.toString();
    }

    public String asTreeString() {
        String treeString = treeString();
        return treeString.substring(0,treeString.length()-1);
    }


    public void save( String einFileName ) throws IOException {
        save(new File(einFileName),false);
    }

    public void save( File einFile ) throws IOException {
        save(einFile,false);
    }

    public void save( String einFileName,boolean forSIC ) throws IOException {
        save(new File(einFileName),forSIC);
    }

    public void save( File einFile,boolean forSIC ) throws IOException {
        String cr = System.getProperties().getProperty("line.separator");
        char[] ch;
        if (forSIC)
            ch = ( "Tree:" + cr +
                   asTreeString() + cr + cr +
                   "ListIndex:" +cr + cr +
                   "VisitList:" ).toCharArray();
          else ch = asTreeString().toCharArray();

        byte[] fileData = new byte[ch.length];

        for (int i=0; i<ch.length; i++) fileData[i] = (byte) ch[i];

        FileOutputStream out = new FileOutputStream(einFile);
        out.write(fileData); out.close();
    }

   public Object clone() {
       SICTree answer = new SICTree();
       answer.state = state;
       answer.value = new String(value);
       if (attributes!=null) {
           answer.attributes = new Hashtable();
           String[] help = getAttributes();
           for (int i=0; i<help.length; i++)
               answer.attributes.put(new String(help[i]),((Vector)attributes.get(help[i])).clone());
       }
       if (sons!=null)
           for (int i=0; i<sons.size(); i++)
               answer.addSonRight((SICTree)((SICTree)sons.elementAt(i)).clone());
       return answer;
   }


// following part is for evaluating the tree


    public void addAttribute( String anAttribute ) {
        if (attributes==null) addAttribute(anAttribute,"false",null);
          else if (attributes.get(anAttribute)==null) addAttribute(anAttribute,"false",null);
    }

    public void addAttribute( String anAttribute,String anAttributeState,Object anAttributeValue ) {
        Vector help = new Vector(2);
            help.addElement(anAttributeState);
            help.addElement(anAttributeValue);
        if (attributes==null) attributes = new Hashtable();
        attributes.put(anAttribute,help);
    }

    public String[] getAttributes() {
        Vector help = new Vector();
        Enumeration attributes = this.attributes.keys();
        while (attributes.hasMoreElements())
            help.addElement(attributes.nextElement());
        String[] answer = new String[help.size()];
        help.copyInto(answer);
        return answer;
    }

    public String getStateOfAttribute( String anAttribute ) {
        if (attributes==null) return null;
          else {
            Vector help = (Vector)attributes.get(anAttribute);
            if (help==null) return null;
              else return (String)help.elementAt(0);
          }
    }

    public Object getValueOfAttribute( String anAttribute ) {
        if (attributes==null) return null;
          else {
            Vector help = (Vector)attributes.get(anAttribute);
            if (help==null) return null;
              else return help.elementAt(1);
          }
    }

    public void setStateOfAttribute( String anAttribute,String anAttributeState ) {
        if (attributes==null) addAttribute(anAttribute,anAttributeState,null);
          else if (attributes.get(anAttribute)==null) addAttribute(anAttribute,anAttributeState,null);
                 else ((Vector)attributes.get(anAttribute)).setElementAt(anAttributeState,0);
    }

    public void setValueOfAttribute( String anAttribute,Object anAttributeValue ) {
        if (attributes==null) addAttribute(anAttribute,"true",anAttributeValue);
          else if (attributes.get(anAttribute)==null) addAttribute(anAttribute,"true",anAttributeValue);
                 else ((Vector)attributes.get(anAttribute)).setElementAt(anAttributeValue,1);
    }


    public String[] getProduction2( boolean nodeIsRightSideOfProduction ) {
        Vector answer = new Vector();
        SICTree help;
        if (nodeIsRightSideOfProduction) help = this;
          else help = this.father;
        if (help!=null) if (help.sons!=null) {
            answer.addElement(help.value);
            for (int i=0; i<help.sons.size(); i++)
                answer.addElement( ((SICTree)help.sons.elementAt(i)).value );
            }
        String[] finalAnswer = null;
        if (answer.size()>0) {
            finalAnswer = new String[answer.size()];
            answer.copyInto(finalAnswer);
        }
        return finalAnswer;
    }

    public void initAttributes() {
        if (isNode() & attributes!=null) {
	    Vector help;
            Enumeration attributes = this.attributes.keys();
            while (attributes.hasMoreElements()) {
              help = (Vector)this.attributes.get((String)attributes.nextElement());
              help.setElementAt("false",0);
              help.setElementAt(null,1);
            }
            for (int i=0; i<sons.size(); i++)
                ((SICTree)sons.elementAt(i)).initAttributes();
        }
    }
}